예제 #1
0
        public static Rect GetSupportRect(FloorPlan floorPlan, IRoomGen oldGen, IRoomGen newGen, Dir4 dir, List <RoomHallIndex> adjacentsInDir)
        {
            bool vertical    = dir.ToAxis() == Axis4.Vert;
            Rect supportRect = newGen.Draw;

            supportRect.Start += dir.GetLoc() * supportRect.Size.GetScalar(dir.ToAxis());
            supportRect.SetScalar(dir, oldGen.Draw.GetScalar(dir));

            IntRange minMax = newGen.Draw.GetSide(dir.ToAxis());

            foreach (RoomHallIndex adj in adjacentsInDir)
            {
                IRoomGen adjGen    = floorPlan.GetRoomHall(adj).RoomGen;
                IntRange adjMinMax = adjGen.Draw.GetSide(dir.ToAxis());
                minMax = new IntRange(Math.Min(adjMinMax.Min, minMax.Min), Math.Max(adjMinMax.Max, minMax.Max));
            }

            IntRange oldMinMax = oldGen.Draw.GetSide(dir.ToAxis());

            minMax = new IntRange(Math.Max(oldMinMax.Min, minMax.Min), Math.Min(oldMinMax.Max, minMax.Max));

            if (vertical)
            {
                supportRect.SetScalar(Dir4.Left, minMax.Min);
                supportRect.SetScalar(Dir4.Right, minMax.Max);
            }
            else
            {
                supportRect.SetScalar(Dir4.Up, minMax.Min);
                supportRect.SetScalar(Dir4.Down, minMax.Max);
            }

            return(supportRect);
        }
예제 #2
0
        public override void ApplyToPath(IRandom rand, GridPlan floorPlan)
        {
            if (floorPlan.GridWidth < 3 || floorPlan.GridHeight < 3)
            {
                throw new InvalidOperationException("Not enough room to create path.");
            }

            int maxRooms = (2 * floorPlan.GridWidth) + (2 * floorPlan.GridHeight) - 4;
            int roomOpen = maxRooms * this.CircleRoomRatio.Pick(rand) / 100;
            int paths    = this.Paths.Pick(rand);

            if (roomOpen < 1 && paths < 1)
            {
                roomOpen = 1;
            }

            GenContextDebug.StepIn("Outer Circle");

            // draw the top and bottom
            for (int xx = 0; xx < floorPlan.GridWidth; xx++)
            {
                this.RollOpenRoom(rand, floorPlan, new Loc(xx, 0), ref roomOpen, ref maxRooms);
                GenContextDebug.DebugProgress("Room");
                this.RollOpenRoom(rand, floorPlan, new Loc(xx, floorPlan.GridHeight - 1), ref roomOpen, ref maxRooms);
                GenContextDebug.DebugProgress("Room");
                if (xx > 0)
                {
                    floorPlan.SetHall(new LocRay4(new Loc(xx, 0), Dir4.Left), this.GenericHalls.Pick(rand), this.HallComponents.Clone());
                    GenContextDebug.DebugProgress("Hall");
                    floorPlan.SetHall(new LocRay4(new Loc(xx, floorPlan.GridHeight - 1), Dir4.Left), this.GenericHalls.Pick(rand), this.HallComponents.Clone());
                    GenContextDebug.DebugProgress("Hall");
                }
            }

            // draw the left and right (excluding the top and bottom)
            for (int yy = 0; yy < floorPlan.GridHeight; yy++)
            {
                // exclude the top and bottom
                if (yy > 0 && yy < floorPlan.GridHeight - 1)
                {
                    this.RollOpenRoom(rand, floorPlan, new Loc(0, yy), ref roomOpen, ref maxRooms);
                    GenContextDebug.DebugProgress("Room");
                    this.RollOpenRoom(rand, floorPlan, new Loc(floorPlan.GridWidth - 1, yy), ref roomOpen, ref maxRooms);
                    GenContextDebug.DebugProgress("Room");
                }

                if (yy > 0)
                {
                    floorPlan.SetHall(new LocRay4(new Loc(0, yy), Dir4.Up), this.GenericHalls.Pick(rand), this.HallComponents.Clone());
                    GenContextDebug.DebugProgress("Hall");
                    floorPlan.SetHall(new LocRay4(new Loc(floorPlan.GridWidth - 1, yy), Dir4.Up), this.GenericHalls.Pick(rand), this.HallComponents.Clone());
                    GenContextDebug.DebugProgress("Hall");
                }
            }

            GenContextDebug.StepOut();

            GenContextDebug.StepIn("Inner Paths");

            Rect innerRect = new Rect(1, 1, floorPlan.GridWidth - 2, floorPlan.GridHeight - 2);

            // create inner paths
            for (int pathsMade = 0; pathsMade < paths; pathsMade++)
            {
                GenContextDebug.StepIn($"Path {pathsMade}");

                Dir4 startDir = DirExt.VALID_DIR4.ElementAt(rand.Next(DirExt.DIR4_COUNT));
                int  x        = rand.Next(innerRect.Start.X, innerRect.End.X);
                int  y        = rand.Next(innerRect.Start.Y, innerRect.End.Y);
                switch (startDir)
                {
                case Dir4.Down:
                    y = 0;
                    break;

                case Dir4.Left:
                    x = floorPlan.GridWidth - 1;
                    break;

                case Dir4.Up:
                    y = floorPlan.GridHeight - 1;
                    break;

                case Dir4.Right:
                    x = 0;
                    break;

                case Dir4.None:
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(startDir), "Invalid enum value.");
                }

                Loc  wanderer   = new Loc(x, y);
                Dir4 prevDir    = Dir4.None; // direction of movement
                int  pathLength = (startDir.ToAxis() == Axis4.Vert) ? innerRect.Height : innerRect.Width;
                for (int currentLength = 0; currentLength < pathLength; currentLength++)
                {
                    Dir4 chosenDir = startDir;

                    // avoid this block the first time
                    if (currentLength > 0)
                    {
                        List <Dir4> dirs = new List <Dir4>();
                        foreach (Dir4 dir in DirExt.VALID_DIR4)
                        {
                            // do not backtrack
                            if (dir == prevDir)
                            {
                                continue;
                            }

                            // do not hit edge
                            if (!Collision.InBounds(innerRect, wanderer + dir.GetLoc()))
                            {
                                continue;
                            }
                            dirs.Add(dir);
                        }

                        chosenDir = dirs[rand.Next(dirs.Count)];
                    }

                    Loc dest = wanderer + chosenDir.GetLoc();

                    GridRoomPlan existingRoom = floorPlan.GetRoomPlan(dest);
                    if (existingRoom == null)
                    {
                        if (currentLength == pathLength - 1) // only the end room can be a non-hall
                        {
                            floorPlan.AddRoom(dest, this.GenericRooms.Pick(rand), this.RoomComponents.Clone());
                        }
                        else
                        {
                            floorPlan.AddRoom(dest, this.GetDefaultGen(), this.HallComponents.Clone(), true);
                        }
                        GenContextDebug.DebugProgress("Room");
                    }
                    else if (existingRoom.PreferHall)
                    {
                        if (currentLength == pathLength - 1)
                        {
                            // override the hall room
                            existingRoom.RoomGen    = this.GenericRooms.Pick(rand).Copy();
                            existingRoom.PreferHall = false;
                        }
                    }

                    floorPlan.SetHall(new LocRay4(wanderer, chosenDir), this.GenericHalls.Pick(rand), this.HallComponents.Clone());
                    GenContextDebug.DebugProgress("Hall");

                    wanderer = dest;
                    prevDir  = chosenDir.Reverse();
                }

                GenContextDebug.StepOut();
            }

            GenContextDebug.StepOut();
        }
예제 #3
0
        protected static ListPathTraversalNode?GetRoomToConnect(FloorPlan floorPlan, RoomHallIndex chosenFrom, Dir4 dir)
        {
            // extend a rectangle to the border of the floor in the chosen direction
            bool     vertical   = dir.ToAxis() == Axis4.Vert;
            int      dirSign    = dir.GetLoc().GetScalar(dir.ToAxis());
            IRoomGen genFrom    = floorPlan.GetRoomHall(chosenFrom).RoomGen;
            Rect     sampleRect = genFrom.Draw;

            // expand from the start of that border direction to the borders of the floor
            sampleRect.Start += dir.GetLoc() * sampleRect.Size.GetScalar(dir.ToAxis());

            // it doesn't have to be exactly the borders so just add the total size to be sure
            sampleRect.Expand(dir, vertical ? floorPlan.Size.Y : floorPlan.Size.X);

            // find the closest room.
            var chosenTo = new RoomHallIndex(-1, false);

            foreach (RoomHallIndex collision in floorPlan.CheckCollision(sampleRect))
            {
                Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw;

                // limit the expansion by direction
                int  sampleScalar   = sampleRect.GetScalar(dir);
                int  collidedScalar = collidedRect.GetScalar(dir.Reverse());
                bool limit          = dirSign == Math.Sign(sampleScalar - collidedScalar);
                if (limit)
                {
                    // update the boundaries
                    sampleRect.SetScalar(dir, collidedScalar);
                    chosenTo = collision;
                }
            }

            // if it didn't collide with ANYTHING, then quit
            if (chosenTo.Index == -1)
            {
                return(null);
            }

            IRoomGen genTo = floorPlan.GetRoomHall(chosenTo).RoomGen;

            // narrow the rectangle if touching something on the side
            // widen the rectangle by width
            Rect widthRect = sampleRect;

            widthRect.Inflate(vertical ? 1 : 0, vertical ? 0 : 1);
            bool retractLeft  = false;
            bool retractRight = false;
            Dir4 leftDir      = DirExt.AddAngles(dir, Dir4.Left);
            Dir4 rightDir     = DirExt.AddAngles(dir, Dir4.Right);

            foreach (RoomHallIndex collision in floorPlan.CheckCollision(widthRect))
            {
                Rect collidedRect = floorPlan.GetRoomHall(collision).RoomGen.Draw;
                if (!retractLeft)
                {
                    if (collidedRect.GetScalar(leftDir.Reverse()) == sampleRect.GetScalar(leftDir))
                    {
                        retractLeft = true;
                    }
                }

                if (!retractRight)
                {
                    if (collidedRect.GetScalar(rightDir.Reverse()) == sampleRect.GetScalar(rightDir))
                    {
                        retractRight = true;
                    }
                }
            }

            // retract the rectangle
            if (retractLeft)
            {
                sampleRect.Expand(leftDir, -1);
            }
            if (retractRight)
            {
                sampleRect.Expand(rightDir, -1);
            }

            // if the rectangle has been retracted too much, we can't go on
            if (sampleRect.Area <= 0)
            {
                return(null);
            }

            // check for border availability between start and end
            bool foundFrom = HasBorderOpening(genFrom, sampleRect, dir);
            bool foundTo   = HasBorderOpening(genTo, sampleRect, dir.Reverse());

            // return the expansion if one is found
            if (foundFrom && foundTo)
            {
                return(new ListPathTraversalNode(chosenFrom, chosenTo, sampleRect));
            }
            else
            {
                return(null);
            }
        }
예제 #4
0
        /// <summary>
        /// In a 4- or 3-way hall situation, this method is called to add the remaining ways after the first two have been added.
        /// </summary>
        /// <param name="map"></param>
        /// <param name="cross"></param>
        /// <param name="possibleStarts"></param>
        /// <param name="vertical"></param>
        /// <param name="turn"></param>
        private void DrawSecondaryHall(T map, HashSet <int> cross, Dictionary <Dir4, List <HashSet <int> > > possibleStarts, bool vertical, bool turn)
        {
            if (!turn)
            {
                // if not turning, use the cross variables
                this.DrawStraightHall(map, cross, vertical);
            }
            else
            {
                Dir4 forwardDir = vertical ? Dir4.Up : Dir4.Left;
                List <HashSet <int> > starts = possibleStarts[forwardDir];
                List <HashSet <int> > ends   = possibleStarts[forwardDir.Reverse()];

                // the chosen tiles to start digging the hall from
                int[] startTiles = new int[starts.Count];
                int[] endTiles   = new int[ends.Count];

                if (starts.Count == 1 && ends.Count == 1)
                {
                    Choose1on1BentHallStarts(map, starts[0], ends[0], startTiles, endTiles);

                    // forward until hit
                    {
                        Loc forwardStart = this.Draw.Start;
                        Loc startLoc     = new Loc(vertical ? startTiles[0] : forwardStart.X, vertical ? forwardStart.Y : startTiles[0]);
                        Loc endLoc       = startLoc;

                        // the assumption is that there is already roomterrain to cross over at another point in this room
                        while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain))
                        {
                            endLoc += forwardDir.Reverse().GetLoc();
                        }
                        this.DrawHall(map, startLoc, endLoc, vertical);
                    }

                    // backward until hit
                    {
                        Loc backwardStart = this.Draw.End - new Loc(1);
                        Loc startLoc      = new Loc(vertical ? endTiles[0] : backwardStart.X, vertical ? backwardStart.Y : endTiles[0]);
                        Loc endLoc        = startLoc;

                        // the assumption is that there is already roomterrain to cross over at another point in this room
                        while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain))
                        {
                            endLoc += forwardDir.GetLoc();
                        }
                        this.DrawHall(map, startLoc, endLoc, vertical);
                    }
                }
                else
                {
                    // if turning, use the respective possible starts and draw until the primary lines are hit
                    foreach (Dir4 dir in DirExt.VALID_DIR4)
                    {
                        // choose vertical starts if vertical, horiz starts if otherwise
                        if ((dir.ToAxis() == Axis4.Vert) == vertical)
                        {
                            for (int jj = 0; jj < possibleStarts[dir].Count; jj++)
                            {
                                int[] crossArray = new int[possibleStarts[dir][jj].Count];
                                possibleStarts[dir][jj].CopyTo(crossArray);
                                int startSideDist = crossArray[map.Rand.Next(crossArray.Length)];
                                Loc forwardStart  = (dir == Dir4.Up || dir == Dir4.Left) ? this.Draw.Start : this.Draw.End - new Loc(1);
                                Loc startLoc      = new Loc(vertical ? startSideDist : forwardStart.X, vertical ? forwardStart.Y : startSideDist);
                                Loc endLoc        = startLoc;

                                // the assumption is that there is already roomterrain to cross over at another point in this room
                                while (!map.GetTile(endLoc).TileEquivalent(map.RoomTerrain))
                                {
                                    endLoc += dir.Reverse().GetLoc();
                                }

                                this.DrawHall(map, startLoc, endLoc, vertical);
                            }
                        }
                    }

                    // there is no guarantee that both sides will have an open bordertile; they'll just come in from their respective directions
                }
            }
        }
예제 #5
0
        private List <List <LocRay4> > findHallSets(GridPlan floorPlan, Rect rect, List <LocRay4> raysOut)
        {
            bool[] raysCovered = new bool[raysOut.Count];

            List <List <LocRay4> > resultList = new List <List <LocRay4> >();

            Graph.GetAdjacents <int> getAdj = (int nodeIndex) =>
            {
                List <int> returnList = new List <int>();

                Loc loc       = new Loc(nodeIndex % floorPlan.GridWidth, nodeIndex / floorPlan.GridWidth);
                int roomIndex = floorPlan.GetRoomIndex(loc);
                for (int dd = 0; dd < DirExt.DIR4_COUNT; dd++)
                {
                    Dir4 dir     = (Dir4)dd;
                    Loc  destLoc = loc + dir.GetLoc();
                    //check against outside floor bound
                    if (!Collision.InBounds(floorPlan.GridWidth, floorPlan.GridHeight, destLoc))
                    {
                        continue;
                    }
                    //check against inside rect bound
                    if (Collision.InBounds(rect, destLoc))
                    {
                        continue;
                    }
                    //check against a valid room
                    int destRoom = floorPlan.GetRoomIndex(destLoc);
                    if (destRoom == roomIndex)
                    {
                        returnList.Add(destLoc.Y * floorPlan.GridWidth + destLoc.X);
                    }
                    else if (destRoom > -1)
                    {
                        if (floorPlan.GetHall(new LocRay4(loc, dir)) != null)
                        {
                            returnList.Add(destLoc.Y * floorPlan.GridWidth + destLoc.X);
                        }
                    }
                }

                return(returnList);
            };

            //group the exits together
            for (int ii = 0; ii < raysOut.Count; ii++)
            {
                if (raysCovered[ii])
                {
                    continue;
                }

                Loc            startLoc = raysOut[ii].Traverse(1);
                List <LocRay4> set      = new List <LocRay4>();

                Graph.TraverseBreadthFirst(startLoc.Y * floorPlan.GridWidth + startLoc.X,
                                           (int nodeIndex, int distance) =>
                {
                    Loc fillLoc = new Loc(nodeIndex % floorPlan.GridWidth, nodeIndex / floorPlan.GridWidth);
                    for (int nn = 0; nn < raysOut.Count; nn++)
                    {
                        if (raysOut[nn].Traverse(1) == fillLoc)
                        {
                            set.Add(raysOut[nn]);
                            raysCovered[nn] = true;
                        }
                    }
                },
                                           getAdj);

                resultList.Add(set);
            }

            return(resultList);
        }