ellipseroomsWork is impinging upon my coding time (how dare it!), so didn’t get a chance to do too much tonight.  I did generalize the level generation code a bit, so that I can swap in different generators.  It’s just a first pass at generatlization, but I created a “RoundRoomGenerator” function alongside the “SquareRoomGenerator” that I previously had, and with no changes to the BSP code the generator can now create different kinds of rooms.  Round Rooms aren’t that interesting themselves, but you can imagine plugging in more complex room generators in the future, without having to worry about impact to partitioning or corridor generation logic.

There are a few errors in the pic; a couple of orphaned rooms and corridors without proper endcaps – these are both due to the DigCorridor logic that I wasn’t happy with.  I have some thoughts on how to clean that up, and hope to have that tomorrow.

So; code changes included:

New Room Generator Delegates

I created a few more roomGenerators, and randomly pick one:

            // Variety is the spice of life; mix things up a bit.
            RoomGeneratorDelegate roomGenerator;

            // Choose square or round rooms (or mix).  In future, have more complex room generators.
            switch (Rand.Next(3))
            {
                case 0:
                    roomGenerator = new RoomGeneratorDelegate(SquareRoomGenerator);
                    break;
                case 1:
                    roomGenerator = new RoomGeneratorDelegate(RoundRoomGenerator);
                    break;
                default:
                    roomGenerator = new RoomGeneratorDelegate(RandomShapeRoomGenerator);
                    break;
            }

New RoundRoomGenerator function

Very similar to the SquareRoomGenerator (hinting at further abstraction to come later). Only change is that when it paints the room, it paints an ellipse instead of a rectangle.

Obviously, I’m not stressing too much about the performance of this, since it’s just for example purposes.

        // Create a room in the area specified by the regionNode.
        public void RoundRoomGenerator(DungeonBSPNode dungeonRegion)
        {
            int MinIdealRoomSize = 7;

            // Convert from absolute normalized coordinates (0.0-1.0) to Map coordinates (0-(MapWidth-1), 0-(MapHeight-1))
            int xRegionStart = (int)Math.Ceiling((dungeonRegion.RegionEdges.Left * (TargetMap.MapWidth - 1)));
            int yRegionStart = (int)Math.Ceiling((dungeonRegion.RegionEdges.Top * (TargetMap.MapHeight - 1)));
            int xRegionEnd = (int)((dungeonRegion.RegionEdges.Right * (TargetMap.MapWidth - 1)));
            int yRegionEnd = (int)((dungeonRegion.RegionEdges.Bottom * (TargetMap.MapHeight - 1)));
            int regionWidth = xRegionEnd - xRegionStart;
            int regionHeight = yRegionEnd - yRegionStart;

            int roomWidth = RandomValueBetween(Math.Min(MinIdealRoomSize, regionWidth), regionWidth);
            int roomHeight = RandomValueBetween(Math.Min(MinIdealRoomSize, regionHeight), regionHeight);

            int xRoomStart = xRegionStart + Rand.Next(regionWidth - roomWidth);
            int yRoomStart = yRegionStart + Rand.Next(regionHeight - roomHeight);

            // Store the room coordinates in the Dungeon Region Node (we'll want them again later for corridor creation)
            dungeonRegion.BoundingRect = new Rectangle(xRoomStart, yRoomStart, roomWidth, roomHeight);
            dungeonRegion.RoomBuilt = true;

            // "Paint" the room into the Map
            TargetMap.PaintCellEllipse(xRoomStart, yRoomStart, xRoomStart + roomWidth, yRoomStart + roomHeight, new Cell_Wall(), true);
            TargetMap.PaintCellEllipse(xRoomStart + 1, yRoomStart + 1, xRoomStart + roomWidth - 1, yRoomStart + roomHeight - 1, new Cell_Floor(), true);
        }

PaintCellEllipse function

It’s not all that relevant, but here’s the function to fill an ellipse that fills the specified bounding rectangle:

        public void PaintCellEllipse(int xStart, int yStart, int xEnd, int yEnd, Cell cell)
        {
            // Draw an ellipse centered in the passed-in coordinates
            float xCenter = (xEnd + xStart) / 2.0f;
            float yCenter = (yEnd + yStart) / 2.0f;
            float radius = Math.Min(xEnd - xStart, yEnd - yStart) / 2.0f;
            float xAxis = (xEnd - xStart) / 2.0f;
            float yAxis = (yEnd - yStart) / 2.0f;
            float majorAxisSquared = (float)Math.Pow(Math.Max(xAxis, yAxis), 2.0);
            float minorAxisSquared = (float)Math.Pow(Math.Min(xAxis, yAxis), 2.0);

            for (int y = yStart; y <= yEnd; y++)
                for (int x = xStart; x <= xEnd; x++)
                {
                    // Only draw if (x,y) is within the ellipse
                    if (Math.Sqrt((x - xCenter) * (x - xCenter) / majorAxisSquared + (y - yCenter) * (y - yCenter) / minorAxisSquared) <= 1.0f)
                        Cells[x, y] = cell;
                }
        }

And just for fun – Mixed rooms

It’s clearly not a big leap to add a MixedRoomGenerator that just randomly selects from the square or round room generator –

        public void RandomShapeRoomGenerator(DungeonBSPNode dungeonRegion)
        {
            if (Rand.Next(2) == 0)
                RoundRoomGenerator(dungeonRegion);
            else
                SquareRoomGenerator(dungeonRegion);
        }

Which generates output like this:
mixedrooms
And that’s it! Not a huge step forward, but an interesting one…

Advertisements