At this point, I’ve seen what sort of layouts I can theoretically produce. It’s a good time to rename my room designations to Halls and Hubs and have them in sizes Big, Medium and Small. This feels more readable and more in line with what they do (also they alphabetize nicely). And I want to add some additional ones for themes and purposes.
I can also trim a bunch of potential rooms from the project, and I want to make as few rooms as I can get away with. Basically, I can have the dungeon generation never produce certain rooms, i.e. rooms of certain designation and certain exits. For example, I would never have halls with 1 exit, because halls connect and don’t form dead-ends. Of course, they could if I told them to, but I would deliberately not do this. Similarly, halls don’t need 4 exits, because I am not making halls with all 4 connections as that might as well be a hub then and I want to avoid such halls. I am already restricting it, but I can add it to the editor so it is clear what’s happening (and before I forget a week from now):
In fact, since I’m externalizing the restrictions and they become important (that is, generation fails if they are not observed), I might as well do all of the ones I have and have them in a full list, for example:
It’s a bit finicky to specify in the editor which steps use which restrictions, but it’s worth it for me to always remember what they do.
After some deliberation I also decided to get rid of different types of hubs, that is, their big and medium variations. I already need 5 versions for each hub depending on the possible entrances. Another size just adds 10 more. I should be balancing the contents via level difficulty, size, distance to start room, and other factors and not trying to define this on a per-room basis. Besides, hubs need content while halls and tunnels barely need any, so I can make many of those much easier.
Furthermore, I also decided to get rid of three hall sizes and just have big and small halls. It just feels like there’s not enough reason to have the small ones. I might eventually add something like “secret hall” rooms, but those would only need one variation and would have their own layout generation step.
I can also make my start room have only 1 exit by placing a hub directly outside it. This means I only need one variations for it. My framework can easily extend and support this:
Same for goal room.
There are also more generation issues that I have been spotting and fixing. For example, bridges can occasionally run parallel to other nodes. And, while not a problem in itself, it becomes a problem with too many nodes:
Here the bridge ran adjacent to three! hubs and they all later connected. I can solve this by adding a new restriction that counts the total number of neighbours a bridge would have and doesn’t allow bridges that have too many:
This completely removes the sort of result as above from happening. In fact, setting it to 0 means the bridges will never touch anything. I might even add this to branches, although their logic is slightly different.
I also decided to use small halls for my bridges:
This means the “extra” connectors are different to the main ones. This just adds some flavour to the dungeon (I think, may be, who knows?):
Another idea I’ve had for a while is to make a larger structure within the dungeon, such as a boss arena. For now, I will call this room designation “arena”. For starters, an arena would be a 2×2 interconnected area. I wasn’t really sure if I can do this with the current generator, because it wants to work with single nodes. (And I am also straying away from grid-based dungeon at that point — I might as well not have hubs and just have halls and arenas as rooms.) But anyway, I now have an arena room:
And I made a corresponding dungeon generation step:
And this places 2×2 sections of arena nodes, which translates into arena rooms (pink spheres):
I really want to limit the number of extra rooms I have to create for this. Ideally, I would have just 1 or 2 rooms (and rotate as needed). In the screenshot, the arenas don’t have connections to the main dungeon yet, because it’s way trickier than I though. The problem is that the arenas connecting to one another is not the same as a hall or hubs connecting to an arena:
To illustrate, there are theoretically 4 possible arena types (green — inter-arena connection; orange – outside connection):
(And this is just arenas that connect in a 2×2 formation. This is why I wanted to avoid making structures from nodes/rooms.)
In order to solve this, I need distinguish between “door” connections and “area” connections. Door connection are all the connections I previously discussed and I will use this as the primary reference point for connecting. Area connections would be optional and only apply to arenas (for now).
The three different layouts of arenas are:
In other words: no door, door on the “right side”, and door on the “left side”. I can specify this by having the secondary “area directions” field. When I need rooms with areas, I also supply this additional value, such as “double area connection” (2 sides next to each other). And I also need to specify the offset of the area. All the doors always start with Up direction, but the areas do only if there are no doors. In other words, I need to know which of the last two room variants from above to choose, because both have a single-side door and a double-side area. So I need to specify where the area connection starts, in my case either 90 or 180 degrees clockwise. For example, the middle room from above is now set up like this:
And with that, I can successfully select the correct arena room based on the incoming door and the “inside area”:
I’m glad I got this working so well, because otherwise it would have meant either rewriting a whole lot of my dungeon generation logic (yikes!), making too many assets and finicking with them (urgh!), or just not having multi-node rooms (aww!).
Now I can focus on making the arenas appear in actually reasonable locations. For example, they can easily spawn next to each other (feels weird gameplay-wise):
Or they can get squished in-between a lot of other nodes (and negate all the steps we took to separate stuff):
So here my generation step restrictions come into play again:
Of course, adding a couple enums is easy. The actual algorithms behind it are a bit more involved. To determine whether there are nodes next to a potential arena I have to create a tile coordinate grid, then collect the bordering coordinates, then check them on the real node map. I am constantly expanding my coordinate utilities with these reusable methods, and I am checking more complex ones with unit tests:
Distance to other nodes is much easier — I just need to find the closest other arena node and see if it’s not closer (I am using visual as-the-crow-flies distance here). I have to be careful and deliberate at writing each of these algorithms here. It’s so easy to introduce some edge case in a pipeline of a dozens of classes and then spend ages trying to find the issue with what are essentially random reproduction steps.
All of this combined, I get some decent dungeons with arenas spaced out nicely:
This means the player has lots of room to fight a big boss or have a special location with loot or something, basically, any point of interest I want to make that could not or should not fit in a single room.
The only thing is that I don’t actually want there to be hub nodes right before every arena. This is a consequence of how I requested my arenas to be generated — from hub nodes. There’s a few approaches I can take here, but I think I will go with something easy. First, I’ll generate a bunch of extra halls from my node:
Then add a new origin option for arenas, which are halls that are either dead-ends or 90 degree turns.
And this produces some decent results:
Of course, this leaves behind some of those dead-end halls whose only purpose was to support additional arenas. One thing I haven’t yet worked on is the various ways to clean up the dungeon layouts. In fact, I suspect I will be regenerating it a lot and spotting weird situations that I want to hard-code some clean up algorithms for. This isn’t ideal in the sense that the cause of those weird situations is my generator’s logic — randomness’ edge cases. But it would take way longer to anticipate and fix it than to clean up the occasional weirdness as it comes along. Besides, none of it is actually game-breaking, just a subpar layout. And I’ve made it easy to add new generation steps. So here’s a step to remove dead-ends:
It finds all hall nodes and if they have just one connection, they get removed and then it recurses to whatever they were connected from. And this works great:
Well, I don’t actually have a screenshot for this. There just aren’t any dead-end halls any more. There aren’t even any options for the logic to discuss or alter. I think this will be a recurring thing when I do the full clean up pass on my dungeon generation.
Next thing to improve for the dungeon will likely be gameplay locations before I resume expanding or cleaning up the generation logic.